# typescript 企业级服务器开发:理论篇
通常小打小闹的 Node.js 服务器开发基本用面条代码就可以了,但是一般用 typescript 开发 Node.js 后端项目,这个要求通常是企业级的,面条代码显然是不能写了,要有基本的分层、逻辑与配置分离等等,这个时候最好的方法是选择一个企业级框架,比如 Nest.js,或者在 egg.js、koa 这种低级别框架、库基础上封装一个企业级框架。
我们在 koa 的基础上封装一个框架的内容之多显然可以写一本书了,而且除非有特殊需求,最好的方式还是选择社区内已经比较成熟的框架进行开发,我们就学习一下 Node.js 生态中为数不多的企业级开发框架 Nest.js。
Nest.js 是 Node 渐进式框架,底层默认使用 express(可以通过 Adapter 转换到 fastify),可以使用 express 或者 fastify 所有中间件,完美支持 TypeScript,它大量借鉴了 Spring 和 Angular 中的设计思想。
我们先学习一下 Nest.js 中几个重要的概念。
# 依赖注入
如果你用过 Spring 或者 Angular,这个概念其实已经听过无数次了。
依赖注入(Dependency Injection,简称DI)是面向对象中控制反转(Inversion of Control,简称 IoC)最常见的实现方式,主要用来降低代码的耦合度。
假设你要造一辆车,你需要引擎和轮子:
import { Engine } from './engine'
import { Tire } from './tire'
class Car {
private engine;
private wheel;
constructor() {
this.engine = new Engine();
this.tire = new Tire();
}
}
这时候 Car 这个类依赖于 Engine 和 Tire,构造器不仅需要把依赖赋值到当前类内部属性上还需要把依赖实例化。假设,有很多种类的 Car 都用了 Engine,这时候需要把 Engine 替换为 ElectricEngine。
以上这种牵一发而动全身的后果,就是代码耦合度过高造成的,因此我们得想办法降低耦合度,这个时候我们就需要用到 IoC。
import { Engine } from './engine'
import { Tire } from './tire'
class Container {
private constructorPool;
constructor() {
this.constructorPool = new Map();
}
register(name, constructor) {
this.constructorPool.set(name, constructor);
}
get(name) {
const target = this.constructorPool.get(name);
return new target();
}
}
const container = new Container();
container.bind('engine', Engine);
container.bind('tire', Tire);
class Car {
private engine;
private tire;
constructor() {
this.engine = container.get('engine');
this.tire = container.get('tire');
}
}
此时,container相当于Car和Engine、Tire之间的中转站,Car不需要自己去实例化一个Engine或者Tire,Car和Engine、Tire之间也就没有了强耦合的关系。
从上面例子看出,在使用 IoC 之前,Car需要Engine或者Tire时需要自己主动去创建Engine或者Tire,此时对Engine或者Tire的创建和使用的控制权都在Car手上。
在使用 IoC 之后,Car和Engine或者Tire之间的联系就切断了,当Car需要Engine或者Tire时,IoC Container会主动创建这个对象给Car使用,此时Car获取Engine或者Tire的行为由主动获取变成了被动获取,控制权就颠倒过来。当Engine或者Tire有任何变动,Car不会受到影响,它们之间就完成了解耦。
当我们需要测试Car时,我们不需要把Engine或者Tire全部new一遍来构造Car,只需要把 mock 的Engine或者Tire, 注入到 IoC 容器中就行。
在 Nestjs 中,通过@Injectable装饰器向 IoC 容器注册:
